#include "file_system/file_reader/mmap_file_reader.h"
#include "util/file_wrapper/file_wrapper.h"

namespace hawking {
namespace indexlib {

MmapFileReader::~MmapFileReader() {
    Close();
}

Status MmapFileReader::Close() {
    munmap(reinterpret_cast<char*>(base_addr_), Length());
    base_addr_ = nullptr;
    close(fd_);
    fd_ = -1;
    return Status::OK;
}

Status MmapFileReader::Init() {
    if (could_upadte_) {
        fd_ = open(file_path_.data(), O_RDWR);
    } else {
        fd_ = open(file_path_.data(), O_RDONLY);
    }
    
    if (fd_ == -1) {
        return Status::IOError;
    }

    ReturnValue<off_t> ret = FslibWrapper::FileLength(file_path_);
    if (ret.Code() != Status::OK || ret.Value() <= 0) {
        return Status::IOError;
    }

    file_length_ = static_cast<size_t>(ret.Value());
    if (could_upadte_) {
        base_addr_ =
            (uint8_t*)mmap(
                0, file_length_, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd_, 0);
    } else {
        base_addr_ =
            (uint8_t*)mmap(0, file_length_, PROT_READ, MAP_SHARED | MAP_LOCKED, fd_, 0);
    }
    
    if (base_addr_ == MAP_FAILED) {
        return Status::IOError;
    }

    return Status::OK;
}

ReturnValue<size_t> MmapFileReader::Write(
    const uint8_t* buffer, size_t length, size_t offset) {
    if (!could_upadte_) {
        return ReturnValue<size_t>{Status::NotSupport, 0};
    }

    if (!buffer || offset + length > file_length_) {
        return ReturnValue<size_t>{Status::InvalidArgs, 0};
    }

    std::memcpy(base_addr_ + offset, buffer, length);
    return ReturnValue<size_t>{Status::OK, length};
}

ReturnValue<size_t> MmapFileReader::Read(
    uint8_t* buffer, size_t length, size_t offset) {
    if (offset + length > Length()) {
        return ReturnValue<size_t>{Status::InvalidArgs, 0};
    }

    std::memcpy(buffer, base_addr_ + offset, length);
    return ReturnValue<size_t>{Status::OK, length};
}
ReturnValue<size_t> MmapFileReader::BatchRead(
    const std::vector<FilePart>& batch) {
    size_t ret = 0;
    for (auto& part : batch) {
        if (part.offset + part.length > Length()) {
            return ReturnValue<size_t>{Status::InvalidArgs, 0};
        }

        std::memcpy(part.outbuf, base_addr_ + part.offset, part.length);
        ret += part.length;
    }

    return ReturnValue<size_t>{Status::OK, ret};
}

}
}